package simpledb.storage;

import simpledb.common.Type;
import simpledb.execution.Predicate;

import java.io.DataOutputStream;
import java.io.IOException;

/**
 * StringField 是 SimpleDB 中表示 字符串字段（STRING_TYPE） 的类。它是接口 Field 的一个具体实现，用于封装数据库中的一列字符串类型数据
 * 这个类的主要功能包括：
 * 1、存储一个固定长度的字符串值；
 * 2、支持字符串截断以适应最大长度限制；
 * 3、提供字符串比较操作（如等于、大于、LIKE 等）；
 * 4、实现字段的序列化（写入到字节流）；
 * 5、返回字段的数据类型（Type.STRING_TYPE）；
 * 6、支持哈希计算、相等性判断等通用方法。
 */
public class StringField implements Field {

    // 序列化版本号
    private static final long serialVersionUID = 1L;

    // 字段的实际字符串值
    private final String      value;

    // 字符串的最大长度限制
    private final int         maxSize;

    /**
     * 获取该字段的字符串值
     *
     * @return 字符串值
     */
    public String getValue() {
        return value;
    }

    /**
     * 构造函数：创建一个指定字符串值的 StringField 对象，并限制其最大长度。
     *
     * @param s       要存储的字符串
     * @param maxSize 最大允许长度，超过部分将被截断
     */
    public StringField(String s, int maxSize) {
        this.maxSize = maxSize;

        if (s.length() > maxSize)
            value = s.substring(0, maxSize); // 截断字符串
        else
            value = s; // 直接使用原字符串
    }

    /**
     * 返回该字段的字符串表示形式
     *
     * @return 字符串值
     */
    public String toString() {
        return value;
    }

    /**
     * 返回该字段的哈希码（基于字符串内容）
     *
     * @return 哈希码
     */
    public int hashCode() {
        return value.hashCode();
    }

    /**
     * 判断两个 StringField 是否相等（基于它们的字符串内容）
     *
     * @param field 要比较的对象
     * @return 如果是 StringField 且内容相同则返回 true，否则 false
     */
    public boolean equals(Object field) {
        if (!(field instanceof StringField))
            return false;
        return ((StringField) field).value.equals(value);
    }

    /**
     * 返回该字段的数据类型
     *
     * @return Type.STRING_TYPE 表示这是一个字符串类型字段
     */
    @Override
    public Type getType() {
        return Type.STRING_TYPE;
    }

    /**
     * 比较当前字段与给定字段的值，根据指定的操作符 op 进行判断。
     *
     * @param op 比较操作符（如 EQUALS, GREATER_THAN, LIKE 等）
     * @param val 要比较的字段对象
     * @return 比较结果（true 或 false）
     * @see Field#compare(Predicate.Op, Field)
     */
    @Override
    public boolean compare(Predicate.Op op, Field val) {
        // 强制转换为 StringField，如果不是会抛出异常
        StringField iVal = (StringField) val;

        // 使用 String.compareTo 方法进行比较
        int cmpVal = value.compareTo(iVal.value);

        switch (op) {
            case EQUALS:
                return cmpVal == 0;
            case NOT_EQUALS:
                return cmpVal != 0;
            case GREATER_THAN:
                return cmpVal > 0;
            case GREATER_THAN_OR_EQ:
                return cmpVal >= 0;
            case LESS_THAN:
                return cmpVal < 0;
            case LESS_THAN_OR_EQ:
                return cmpVal <= 0;
            case LIKE:
                // LIKE 操作符：判断是否包含某个子字符串
                return value.contains(iVal.value);
        }

        // 默认不会执行到这里
        return false;
    }

    /**
     * 将该字段的值序列化到 DataOutputStream 中
     *
     * 写入格式：
     * - 首先写入字符串长度（int，4字节）
     * - 然后写入字符串本身（按字节写入）
     * - 最后用 0 填充至 maxSize 长度
     *
     * @param dos 数据输出流
     * @throws IOException 如果写入失败
     */
    @Override
    public void serialize(DataOutputStream dos) throws IOException {
        String s = value;
        int overflow = maxSize - s.length();

        // 如果超出最大长度，则截断
        if (overflow < 0) {
            s = s.substring(0, maxSize);
        }

        // 写入字符串长度
        dos.writeInt(s.length());

        // 写入字符串内容
        dos.writeBytes(s);

        // 填充 0 字节直到达到 maxSize
        while (overflow-- > 0)
            dos.write((byte) 0);
    }
}
